﻿#define DEBUG

using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Mime;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using DocumentFormat.OpenXml.Wordprocessing;
using HtmlAgilityPack;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Exception = System.Exception;
using DocumentFormat.OpenXml.Office;
using Color = System.Drawing.Color;

namespace Cliqsafe
{
    public partial class ThisAddIn
    {

        // GLOBALS

        internal bool MessageBoxFlag { get; set; }
        internal Microsoft.Office.Interop.Outlook.MailItem MailItem { get; set; }
        internal string fc { get; set; }
        internal string Jresults { get; set; }
        internal string CurrentEmail { get; set; }
        internal int Tags { get; set; }
        internal int Tags2 { get; set; }
        internal bool BL { get; set; }
        internal bool CT { get; set; }
        internal float CTI { get; set; }
        internal bool Flag { get; set; }
        public Website Ws { get; set; }
        internal string CurrentUrl { get; set; }
        internal string CurrentInnerHtml { get; set; }

        internal List<string> CurrentUrls = new List<string>();

        internal List<string> HrefAttributes = new List<string>();

        internal List<string> InnerHtml = new List<string>();

        internal string _myMail = null;

        private Microsoft.Office.Interop.Outlook.Explorer _currentExplorer = null;

        //Outlook.Application _outlookApp = new Outlook.Application();
        //Outlook.Inspectors _inspectors;

        internal Microsoft.Office.Interop.Outlook.MailItem TheCurrentMailitem;

        internal static int DebugThisAddInStartup = 0;
        internal static int DebugCurrentExplorerEvent = 0;
        internal static int DebugChangeMailToHtml = 0;
        internal static int DebugGetUrLsStart = 0;
        internal static int DebugGetURLsLoop = 0;
        internal static int DebugGetUrLSEnd = 0;
        internal static int DebugSendUrLtoWot = 0;
        internal static int DebugWoTReplyParse = 0;
        internal static int DebugParseJsonStart = 0;
        internal static int DebugParseJsonEnd = 0;
        internal static int DebugColorTextStart = 0;
        internal static int DebugColorTextEnd = 0;
        internal static int DebugSiteTrustAlgorithmStart = 0;
        internal static int DebugSiteTrustAlgorithmSwitch = 0;
        internal static int DebugSiteTrustAlgorithmEnd = 0;

        internal bool UrLsSentToWot { get; set; }

        /*
        protected override Microsoft.Office.Core.IRibbonExtensibility CreateRibbonExtensibilityObject()
        {
            return new Ribbon();
        }
        */
        private void ThisAddIn_Startup(object sender, System.EventArgs e)
        {
            //_inspectors = this.Application.Inspectors;
            //_inspectors.NewInspector += new Outlook.InspectorsEvents_NewInspectorEventHandler(Inspectors_NewInspector);

#if DEBUG
DebugThisAddInStartup++;
#endif
            _currentExplorer = this.Application.ActiveExplorer();
            _currentExplorer.SelectionChange += new Microsoft.Office.Interop.Outlook
                .ExplorerEvents_10_SelectionChangeEventHandler
                (CurrentExplorer_Event); 
        }
        private void CurrentExplorer_Event()
        {
            CurrentUrls.Clear();
            HrefAttributes.Clear();
            InnerHtml.Clear();
            CurrentInnerHtml = "";
            CurrentUrl = "";

            try
            {
                if (this.Application.ActiveExplorer().Selection.Count > 0)
                {
                    Object selObject = this.Application.ActiveExplorer().Selection[1];
                    if (selObject is Microsoft.Office.Interop.Outlook.MailItem)
                    {
#if DEBUG
DebugCurrentExplorerEvent++;
#endif
                        TheCurrentMailitem = (selObject as Microsoft.Office.Interop.Outlook.MailItem);
                        _myMail = TheCurrentMailitem.HTMLBody;
                        ChangeMailToHtml(_myMail);
                    }
                }
            }

            catch
                (Exception ex)
            {
                string expMessage = ex.Message;
                throw new Exception("Additional information...", ex);

                //MessageBox.Show(expMessage);
            }
        }
        private void ChangeMailToHtml(string mail)
        {
            var doc = new HtmlAgilityPack.HtmlDocument() { };
            doc.OptionFixNestedTags = true;

            try
            {
                doc.LoadHtml(_myMail);

                if (doc.DocumentNode.SelectNodes("//a[@href]").Count > 0)
                //if (doc.DocumentNode.SelectNodes("//a[@href]").Any())
                {

#if DEBUG
DebugChangeMailToHtml++;
#endif

                    GetUrLs(doc);
                }

            }
             catch (Exception ex)
            {
                string expMessage = ex.Message;
                throw new Exception(" Exception in ChangeMailToHtml...", ex);
            }


        }
        private bool GetUrLs(HtmlAgilityPack.HtmlDocument doc)
         {

#if DEBUG
DebugGetUrLsStart++;
#endif
            try
                {
                    
                        // ParseErrors is an ArrayList containing any errors from the Load statement

                        var errorList = new ArrayList();

                        if (doc.ParseErrors != null && doc.ParseErrors.Any())
                        {
                            foreach (HtmlAgilityPack.HtmlParseError error in doc.ParseErrors)
                            {
                                errorList.Add(error.ToString());
                            }
                        }

                        int i = 0;

                        if (doc.DocumentNode.SelectNodes("//a[@href]").Count() != 0)
                        {
                            foreach (HtmlAgilityPack.HtmlNode link in doc.DocumentNode.SelectNodes("//a[@href]"))
                            {
                                HtmlAgilityPack.HtmlAttribute att = link.Attributes["href"];

                                // need to check for page w/ no links - getting a null ref exception here
#if DEBUG
                                DebugGetURLsLoop++;
#endif
                                var url = new Uri(new Uri(att.Value).GetLeftPart(UriPartial.Path));

                                //need to set urls to global property List<string> to allow us to color each link after CTI is determined
                                CurrentUrl = url.ToString();
                                if (CurrentUrl != null)
                                {
                                    CurrentUrls.Add(url.Host);
                                }

                                CurrentInnerHtml = link.InnerHtml;
                                InnerHtml.Add(CurrentInnerHtml);

                                HrefAttributes.Add(att.Value);
                            }
                        }
#if DEBUG
                        DebugGetUrLSEnd++;
#endif
                    
                    Tags = CurrentUrls.Count; // hosts count
                    Tags2 = HrefAttributes.Count; // attributes count

                    if (Tags > 0 && !UrLsSentToWot)
                    {
                        SendUrLtoWot(CurrentUrls);
                        if (!SendUrLtoWot(CurrentUrls))
                        {
                            MessageBox.Show(@"Web Service is temporarily unavailable.
                                         Please ensure that you have internet connectivity");
                        }
                    }
                }

                catch
                    (System.UriFormatException ufe)
                {
                    string expMessage = ufe.Message;
                    CurrentUrl = null;
                    InnerHtml = null;
                    HrefAttributes = null;
                    return false;

                    //MessageBox.Show(ufe.Message);
                }
                catch (Exception e)
                {
                    string expMessage = e.Message;
                    return false;
                    //MessageBox.Show(e.Message);
                }
            
            return true;
        }
    

            // Sends URLs to Web of Trust (WoT) w/ i5 key
            // need to change this to suck up urls from method above
        private bool SendUrLtoWot(IEnumerable<string> hreftags )
        {
#if DEBUG
DebugSendUrLtoWot++;
#endif
            UrLsSentToWot = false;
            fc = "00ff00";
            var request = new Request();
            var sb = new StringBuilder();

            foreach (string t in CurrentUrls)
            {
                sb.Append(t).Append("/");
            }

            request.hosts = sb.ToString();
            request.callback = "process";
            request.key = "f93da20ffcf1f32d75266705081d490027d11666";

            string req = "http://api.mywot.com/0.4/public_link_json2?" + "hosts=" + request.hosts + "&callback=" + request.callback + "&key=" + request.key;

            //string strNew = WebUtility.HtmlEncode(req);

            try
            {

                var httpWebRequest = (HttpWebRequest)WebRequest.Create(req);
                httpWebRequest.ContentType = "application/json";
                httpWebRequest.Method = "GET";

                var httpResponse = (HttpWebResponse)httpWebRequest.GetResponse();

                using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
                {
                    var responseText = streamReader.ReadToEnd();
                    
                    Jresults = responseText.Substring(8, (responseText.Length - 9));
                    var reader = new JsonTextReader(new StringReader(Jresults));
                    WoTReplyParse(Jresults);
                }
            }
            catch (WebException e)
            {
                MessageBox.Show(e.ToString());
                throw new Exception("Error in SendURLtoWOT Method...", e);
            }
            //response = true.
            //no response = false
            UrLsSentToWot = true;
            return true;

        }

        public void WoTReplyParse(string jresults)
        {

#if DEBUG
DebugWoTReplyParse++;
#endif
            
            try
            {
                var list = new List<string>();
                var jlist = new List<JToken>();
                var json = Jresults;
                var jt = JToken.Parse(json);

                foreach (JToken child in jt.Children())
                {
                    var property = child as JProperty;

                    if (property != null)
                    {
                        list.Add(child.ToString());
                        jlist.Add(child);
                    }
                }
                ParseJson(list);
            }
            catch (Exception e)
            {
                {
                    MessageBox.Show(e.ToString());
                    throw new Exception("Error in JsonParse Method...", e);
                }
            }
        }

        private void ParseJson(List<string> list) //have list w/ list.count items
        {

            var ws = new Website();

#if DEBUG
DebugParseJsonStart++;
#endif

            try
            {

               foreach (string url in list)
                {
                    CT = false;
                    BL = false;


                    var categories = new Dictionary<int, int>();
                    var blacklists = new Dictionary<string, int>();

                    var jt = url;
                    string[] lines = Regex.Split(jt, "\r\n");
                    string lines2 = lines[0].Substring(1, (lines[0].Length - 5));
                    string jt2 = "{ " + jt + " }"; // valid json per jslint
                    string json = JsonConvert.SerializeObject(jt2);
                    JToken token = JObject.Parse(jt2);
                    JToken token2 = token.First;

                    JObject sitetoken = token[lines2].Value<JObject>(); // wsName

                    var target = sitetoken["target"]; // ws.Target
                    ws.Target = target.ToString();

                    var trust = sitetoken["0"]; // ws.Trust & ws. TrustConf
                    if (trust != null)
                    {
                        var tv = trust[0]; // ws.Trust
                        var tc = trust[1]; // ws. TrustConf

                        ws.Trust = Convert.ToInt32(tv);
                        ws.TrustConfidence = Convert.ToInt32(tc);
                    }

                    int value = 0;
                    if (jt2.Contains("categories"))
                    {
                        CT = true;
                        
                        categories = JsonConvert.DeserializeObject<Dictionary<int, int>>(sitetoken["categories"].ToString());

                        if (!(categories.TryGetValue(categories.Count, out value))) ;
                        {
                            foreach (var pair in categories)
                            {
                                ws.CategoriesTrust = pair.Key;
                                ws.CategoriesConfidence = pair.Value; 
                            }
                        }
                    }


                    if (jt2.Contains("blacklists"))
                    {
                        BL = true;

                        int j = 0;
                        string[] black = new string[] {};
                        int[] ts = new int[] {};

                        blacklists =
                            JsonConvert.DeserializeObject<Dictionary<string, int>>(sitetoken["blacklists"].ToString());

                        ws.Blacklists = blacklists;

                        if (!(blacklists.TryGetValue(ws.Bl, out value))) ;
                        {

                            {
                                foreach (var pair in blacklists)
                                {
                                    black[j] = pair.Key;
                                    ts[j] = pair.Value;

                                    ws.Bl = black[j];
                                    ws.Ts = ts[j];
                                    j++;
                                }

                            }
                        }
                    }
#if DEBUG
DebugParseJsonEnd++;
#endif
                    SiteTrustAlgorithm(ws.CategoriesConfidence, ws.CategoriesTrust);
                    }
            }
            catch (Exception e)
            {
                {
                    MessageBox.Show(e.ToString());
                    throw new Exception("Error in ParseList Method...", e);
                }
            }
        }

        private SdtElement ConvertStringToSdtElement(string currentString)
        {
            SdtElement stdelement = new SdtBlock();





            return stdelement;
        }

        public void SetRedText(SdtElement element)
        {

#if DEBUG
DebugColorTextStart++;
#endif


                if (element != null)
                {
                    var firstOrDefault = element.Descendants<RunProperties>().FirstOrDefault();
                    if (firstOrDefault != null)
                        firstOrDefault.Color =
                            new DocumentFormat.OpenXml.Wordprocessing.Color() { Val = "0000ffff" };
                }


#if DEBUG
DebugColorTextEnd++;
#endif

        }
      
        private bool SiteTrustAlgorithm(int cata, int confidence)
        {
            
#if DEBUG
DebugSiteTrustAlgorithmStart++;
#endif
            const int _DANGER = 100;
            const int _NEUTRAL = 25;
            const int _BAD = 50;
            const int _GOOD = 0;

            bool value = false;

            if (BL)
            {
                // Site is blacklisted by Web of Trust
                
                if (MessageBoxFlag)
                {
                    MessageBox.Show(CurrentUrl + @"Has been blacklisted by Web of Trust\r\n");
                }
                
                
                
                SetRedText(CurrentInnerHtml);
                
                Ws.Dispose();
                return true;
            }

            if (CT)
            {

                if (Flag)
                {
                    CTI = 0F; //Combined Threat Index
                    float TI = 100F - Ws.Trust;
                    float TC = Ws.TrustConfidence / 100F;

                    CTI = 50F - (TC * TI);
                }
                // modify category w/ confidence modifier
                // category confidence is simply a decimal input into CTI subtraction
                // ex. confidence = 90 = CTI * 0.9

                
#if DEBUG
DebugSiteTrustAlgorithmSwitch++;
#endif

                switch (cata)

                {

                    case 101:
                        // Malware Site
                        CTI = (CTI - (_DANGER * (confidence / 100)));
                        fc = "ffff0000";
                        if (MessageBoxFlag)
                        {
                            MessageBox.Show(CurrentUrl + @"Has been blacklisted by Web of Trust\r\n");
                        }
                        break;
                    case 103:
                        // Phishing Site
                        CTI = (CTI - (_DANGER * (confidence / 100)));
                        fc = "ffff0000";
                        if (MessageBoxFlag)
                        {
                            MessageBox.Show(CurrentUrl + @"Has been blacklisted by Web of Trust\r\n");
                        }
                        break;
                    case 205:
                        // SPAM Site
                        CTI = (CTI - (_DANGER * (confidence / 100)));
                        fc = "ffff0000";
                        if (MessageBoxFlag)
                        {
                            MessageBox.Show(CurrentUrl + @"Has been blacklisted by Web of Trust\r\n");
                        }
                        break;
                    case 206:
                        // Adware Site
                        CTI = (CTI - (_DANGER * (confidence / 100)));
                        break;
                    case 104:
                        // Scam Site
                        CTI = (CTI - (_DANGER * (confidence / 100)));
                        fc = "ffff0000";
                        if (MessageBoxFlag)
                        {
                            MessageBox.Show(CurrentUrl + @"Has been blacklisted by Web of Trust\r\n");
                        }
                        break;
                    case 105:
                        // Illegal Site
                        CTI = (CTI - (_DANGER * (confidence / 100)));
                        fc = "ffff0000";
                        if (MessageBoxFlag)
                        {
                            MessageBox.Show(CurrentUrl + @"Has been blacklisted by Web of Trust\r\n");
                        }
                        break;
                    case 204:
                        // Hate Site
                        CTI = (CTI - (_BAD * (confidence / 100)));
                        break;
                    case 401:
                        // Adult Site
                        CTI = (CTI - (_NEUTRAL * (confidence / 100)));
                        break;
                    case 203:
                        // Generic Suspicious Site
                        CTI = (CTI - (_BAD * (confidence / 100)));
                        break;
                    case 202:
                        // Privacy Concerns Site
                        CTI = (CTI - (_BAD * (confidence / 100)));
                        break;
                    case 303:
                        // Opinions, Religion, Politics Site
                        CTI = (CTI - (_NEUTRAL * (confidence / 100)));
                        break;
                    case 501:
                        // Good Site
                        CTI = (CTI + (_GOOD * (confidence / 100)));
                        break;
                }
            }

#if DEBUG
DebugSiteTrustAlgorithmEnd++;
#endif

            SetRedText(CurrentInnerHtml);
            return true;
        }

        public void DisplayMembers(List<String> hrefTags)
        {
            var text = hrefTags.Aggregate(string.Empty, (current, s) => current + (s.ToString(CultureInfo.InvariantCulture) + "\r\n"));
        }

        private void ThisAddIn_Shutdown(object sender, System.EventArgs e)
        {
        }

        #region VSTO generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InternalStartup()
        {
            this.Startup += new System.EventHandler(ThisAddIn_Startup);
            this.Shutdown += new System.EventHandler(ThisAddIn_Shutdown);
        }
        
        #endregion

        
    }


    public class Website : IDisposable

    {
        private bool _disposed;

        [JsonProperty("0")]
        public int Trust { get; set; }

        [JsonProperty("1")]
        public int TrustConfidence { get; set; }

        [JsonProperty("target")]
        public string Target { get; set; }

        [JsonProperty("categories")]
        public Dictionary<string, int> Categories { get; set; }

        [JsonProperty("CategoriesTrust")]
        public int CategoriesTrust { get; set; }

        [JsonProperty("CategoriesConfidence")]
        public int CategoriesConfidence { get; set; }
         
        [JsonProperty("blacklists")]
        public Dictionary<string, int> Blacklists { get; set; }

        [JsonProperty("Blacklist")]
        public string Bl { get; set; }

        [JsonProperty("Timestamp")]
        public int Ts { get; set; }

        public void Dispose()
        {
            Dispose(true);

            // Use SupressFinalize in case a subclass 
            // of this type implements a finalizer.
            GC.SuppressFinalize(this);
        }
        protected virtual void Dispose(bool disposing)
        {
            if (!_disposed)
            {
                if (disposing)
                {
                    // Clear all property values that maybe have been set
                    // when the class was instantiated
                    Trust = 0;
                    TrustConfidence = 0;
                    Target = String.Empty;
                    //Categories = String.Empty;
                }

                // Indicate that the instance has been disposed.
                _disposed = true;
            }
        }


    }

    [JsonObject(Title = "Request")]
    public class Request
    {
        [JsonProperty(PropertyName = "hosts")]
        public string hosts { get; set; }

        [JsonProperty(PropertyName = "callback")]
        public string callback { get; set; }

        [JsonProperty(PropertyName = "key")]
        public string key { get; set; }
    }
}
